#include "BankSystem.hh"
#include "AccountUtils.hh"
#include "IO.hh" 
#include <iostream>
#include <fstream>
#include <iomanip>
#include <vector>
#include <ctime>
#include <limits>
#include <sstream>

namespace Bank {
    BankSystem& BankSystem::getInstance() {
        static BankSystem instance;
        return instance;}
//After registering a new customer, simultaneously add their two accounts to the unified account list in the system.
    void BankSystem::registerCustomer(const std::string& name) {
        auto c = std::make_shared<Customer>(name);
        customers.push_back(c);
        accounts.push_back(c->getSavingsAccount());
        accounts.push_back(c->getCheckingAccount());
        std::cout << "\nSystem response:\nCustomer " << name << " successfully registered!\n"
                << "- Savings Account: " << c->getSavingsAccount()->getAccountId() << "\n"
                << "- Checking Account: " << c->getCheckingAccount()->getAccountId() << "\n";}

    std::shared_ptr<Customer> BankSystem::findCustomer(const std::string& name) {
        for (auto& c : customers)
            if (c->getName() == name) return c;
        return nullptr;}

    void BankSystem::displayAllAccounts() const {
        double total = 0;
        std::cout << "===== All Bank Accounts =====\n\nSAVINGS ACCOUNTS:\n";
        for (auto& c : customers) {
            auto acc = c->getSavingsAccount();
            std::cout << "ID: " << acc->getAccountId() << " | Owner: " << acc->getOwnerName()<< " | Balance: (CNY)" << std::fixed << std::setprecision(2) << acc->getBalance() << "\n";
            total += acc->getBalance();}

        std::cout << "\nCHECKING ACCOUNTS:\n";
        for (auto& c : customers) {
            auto acc = c->getCheckingAccount();
            std::cout << "ID: " << acc->getAccountId() << " | Owner: " << acc->getOwnerName()<< " | Balance: (CNY)" << std::fixed << std::setprecision(2) << acc->getBalance() << "\n";
            total += acc->getBalance();}

        std::cout << "\nTotal Accounts: " << customers.size() * 2<< "\nTotal Customers: " << customers.size()<< "\nTotal Balance: (CNY)" << std::fixed << std::setprecision(2) << total << "\n";

        std::cout << "\nReturn to main menu? (y/n): ";
        if (!IO::getYesOrNo()) return;}

    void BankSystem::recordTransaction(const Transaction& tx) {
        allTransactions.push_back(tx);}

    const std::vector<Transaction>& BankSystem::getAllTransactions() const {
        return allTransactions;}

    std::vector<Transaction> BankSystem::getTransactionsForAccount(const std::string& id) const {
        std::vector<Transaction> res;
        for (const auto& tx : allTransactions)
            if (tx.involvesAccount(id)) res.push_back(tx);
        return res;}

    std::shared_ptr<IAccount> BankSystem::findAccountById(const std::string& id) const {
        for (const auto& c : customers)
            for (const auto& acc : {
                std::static_pointer_cast<IAccount>(c->getSavingsAccount()),
                std::static_pointer_cast<IAccount>(c->getCheckingAccount()) })
                if (acc && acc->getAccountId() == id)
                    return acc;
        return nullptr;}
//Output the transaction records of the specified account to a text file named with the date and account ID.
    void BankSystem::generateAccountReport(const std::string& id) const {
        auto txs = getTransactionsForAccount(id);
        std::time_t now = std::time(nullptr);
        char date[11];
        std::strftime(date, sizeof(date), "%Y-%m-%d", std::localtime(&now));
        std::ofstream ofs("transactions_" + id + "_" + date + ".txt");
        for (const auto& tx : txs) ofs << tx.toString() << "\n";
        std::cout << "Report saved to: transactions_" << id << "_" << date << ".txt\n"; }

    void BankSystem::generateGlobalReport() const {
        std::time_t now = std::time(nullptr);
        std::ostringstream oss;
        oss << std::put_time(std::localtime(&now), "%Y-%m-%d");
        std::ofstream out("transactions_" + oss.str() + ".txt");
        if (!out) {
            std::cerr << "Error writing report!\n";
            return;}
        for (const auto& tx : allTransactions)
            out << tx.toString() << "\n";
        std::cout << "Report generated.\nReturn to menu? (y/n): ";
        if (!IO::getYesOrNo()) std::cout << "Staying on current page.\n";}

    const std::vector<std::shared_ptr<Customer>>& BankSystem::getAllCustomers() const {
        return customers;}

    std::vector<std::shared_ptr<IAccount>> BankSystem::findAccountsByOwner(const std::string& ownerName) {
        std::vector<std::shared_ptr<IAccount>> result;
        for (const auto& acc : accounts) {
            if (acc->getOwnerName() == ownerName) {
                result.push_back(acc);}
        }
        return result;}

    void BankSystem::handleCustomerRegistration() {
        std::string name;
        std::cout << "Enter customer name (letters/digits only): ";
        while (!(name = IO::getString()).size() || name.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") != std::string::npos) {
            std::cout << "Invalid! Letters and digits only: ";
        }
        registerCustomer(name);}

    void BankSystem::accessCustomerAccounts() {
        std::cout << "\n===== Access Customer Accounts =====\nEnter customer name: ";
        std::string name = IO::getString();
        auto c = findCustomer(name);
        if (!c) {
            std::cout << "Customer not found!\n";
            return;}

        auto s = c->getSavingsAccount();
        auto ch = c->getCheckingAccount();
        while (true) {
            std::cout << "\n===== Customer: " << name << " =====\n"<< "1. Savings: " << s->getAccountId() << " | " << s->getFormattedBalance() << "\n"<< "2. Checking: " << ch->getAccountId() << " | " << ch->getFormattedBalance() << "\n"
                    <<"\n"<< "Options:\n1. Access Savings\n2. Access Checking\n3. View Transactions\n"<< "4. Generate Report\n5. Return to Main Menu\nChoose: ";
            int choice = IO::getNumber();

            if (choice == 1) {
                s->accessAccountInterface(name, allTransactions);
            } else if (choice == 2) {
                ch->accessAccountInterface(name, allTransactions);
            } else if (choice == 3) {
                for (const auto& tx : allTransactions) {
                    if (tx.involvesCustomer(c)) tx.display();}
            } else if (choice == 4) {
                std::string file = "transactions_" + name + "_" + getCurrentDate() + ".txt";
                std::ofstream out(file);
                for (const auto& tx : allTransactions) {
                    if (tx.involvesCustomer(c)) out << tx.toString() << "\n";}
                std::cout << "Report generated: " << file << "\n";//After the user chooses to generate the report, name it according to the customer name and date, and output all transactions for that customer.
            } else if (choice == 5) {
                std::cout << "Returning to main menu...\n";
                return;
            } else {
                std::cout << "Invalid option.\n";
            }
        }
    }
}